import { window, ExtensionContext, QuickInputButton, Uri, QuickPickItem, QuickInput, QuickInputButtons, CancellationToken, Disposable } from 'vscode';
import { compileLogger } from './logger/compileLogger';
import { componentCompileLogger } from './logger/componentCompileLogger';
import * as vscode from 'vscode';
import { spawn } from 'child_process';
import path from 'path';
import fs from 'fs';
import { getRepoPath } from './utils/commonUtils';


let isCompiling = false;
export async function componentCompile(context: ExtensionContext, args: any[]) {
    if (isCompiling) {
        vscode.window.showErrorMessage('Component is already compiling, please wait for the current compile to finish');
        return;
    }

	const title = 'Component Compile';

	const state = await collectInputs(title);
    
	if (args.length === 0) {
        vscode.window.showErrorMessage('No args provided, please see the log for more information');
        componentCompileLogger.errorWithStack('No args provided');
        return;
    }

    const uri = args[0] as vscode.Uri;
    if (!uri || typeof uri.fsPath !== 'string') {
        vscode.window.showErrorMessage('Invalid URI provided, , please see the log for more information');
        componentCompileLogger.errorWithStack('Invalid URI provided');
        return;
    }

    let filePath = uri.fsPath;
    
    // Check if path is a file, if not, append bundle.json to it
    if (fs.existsSync(filePath) && fs.statSync(filePath).isDirectory()) {
        filePath = `${filePath}/bundle.json`;
        if (!fs.existsSync(filePath)) {
            vscode.window.showErrorMessage('Could not find bundle.json in the directory, please see the log for more information');
            componentCompileLogger.errorWithStack('Could not find bundle.json in the directory');
            return;
        }
    }

    componentCompileLogger.info(`bundle.json's path is : ${filePath}`);
    const pathParts = filePath.split(/[/\\]/);
    const parentFolderName = pathParts.length >= 2 ? pathParts.slice(-2, -1)[0] : '';

    if (!parentFolderName) {
        vscode.window.showErrorMessage('Could not extract parent folder name from path, please see the log for more information');
        componentCompileLogger.errorWithStack('Could not extract parent folder name from path');
        return;
    }
    componentCompileLogger.info(`parentFolderName is : ${parentFolderName}`);

    const dirPath = filePath.substring(0, filePath.lastIndexOf('/'));
    const repoRootPath = getRepoPath(dirPath);
    if (!repoRootPath) {
        componentCompileLogger.error('Could not find .repo directory in any parent folder');
        vscode.window.showErrorMessage(`[${parentFolderName}]: Could not find .repo directory`);
        return;
    }

    isCompiling = true;
    vscode.window.withProgress({
        location: vscode.ProgressLocation.Notification,
        title: `[${parentFolderName}]: Begin to Compile...`,
        cancellable: true
    },
    async (progress, token) => {
        try {
            // 执行命令
            const result = await executeAndLogCommand(state, dirPath, parentFolderName, repoRootPath, token);

            if (result && result.status === 'success') {
                const relativePath = readBundleJson(dirPath);
                if (!relativePath) {
                    vscode.window.showErrorMessage(`create build directory failed, please check the log for more information`);
                    return true;
                }
                
                const buildDirPath = createBuildDirectory(dirPath);
                const path = require('path');
                const outDirPath = createDirectory(path.join(buildDirPath, 'out'));
                const srcDirPath = createDirectory(path.join(outDirPath, 'src'));
                const testDirPath = createDirectory(path.join(outDirPath, 'test'));

                
                try {
                    // build/out/src/subsystem/
                    const buildSrcRelativePathParent = path.join(srcDirPath, path.dirname(relativePath));
                    const buildTestRelativePathParent = path.join(testDirPath, path.dirname(relativePath));
                    
                    // 递归创建父目录
                    fs.mkdirSync(buildSrcRelativePathParent, { recursive: true });
                    fs.mkdirSync(buildTestRelativePathParent, { recursive: true });
                    
                    componentCompileLogger.info(`Created parent directory: ${buildSrcRelativePathParent}`);
                    componentCompileLogger.info(`Created parent directory: ${buildTestRelativePathParent}`);
                    
                    // 获取目标路径（最后一级目录名称）
                    const targetDirName = path.basename(relativePath);
                    // build/out/src/subsystem/name
                    const buildSrcSubsystemComponentPath = path.join(buildSrcRelativePathParent, targetDirName);
                    const buildTestSubsystemComponentPath = path.join(buildTestRelativePathParent, targetDirName);
                    
                    // 源目录路径
                    const outDefaultPath = path.join(repoRootPath, 'out', 'default');
                    // out/default/src/subsystem/name
                    const outDefaultSrcSubsystemComponentPath = path.join(outDefaultPath, 'src', relativePath);
                    const outDefaultTestSubsystemComponentPath = path.join(outDefaultPath, 'test', relativePath);
                    
                    // 额外的源目录路径
                    const outDefaultSrcExeUnstrippedSubsystemComponentPath = path.join(outDefaultPath, 'src', 'exe.unstripped', relativePath);
                    const outDefaultSrcLibUnstrippedSubsystemComponentPath = path.join(outDefaultPath, 'src', 'lib.unstripped', relativePath);
                    const outDefaultSrcNoticeFilesSubsystemComponentPath = path.join(outDefaultPath, 'src', 'NOTICE_FILES', 'static', 'ohos_clang_arm', relativePath);
                    const outDefaultSrcSaProfileSubsystemComponentPath = path.join(outDefaultPath, 'src', 'sa_profile', 'inputs', targetDirName);
                    
                    // src
                    createSymlink(outDefaultSrcSubsystemComponentPath, buildSrcSubsystemComponentPath);
                    
                    // test
                    createSymlink(outDefaultTestSubsystemComponentPath, buildTestSubsystemComponentPath);
                    
                    // 为src/exe.unstripped目录创建符号链接
                    createSymlink(outDefaultSrcExeUnstrippedSubsystemComponentPath, path.join(srcDirPath, 'exe.unstripped', relativePath));
                    
                    
                    // 为src/lib.unstripped目录创建符号链接
                    createSymlink(outDefaultSrcLibUnstrippedSubsystemComponentPath, path.join(srcDirPath, 'lib.unstripped', relativePath));

                    
                    // 为src/NOTICE_FILES目录创建符号链接
                    createSymlink(outDefaultSrcNoticeFilesSubsystemComponentPath, path.join(srcDirPath, 'NOTICE_FILES', 'static', 'ohos_clang_arm', relativePath));
                        
                    // 为src/sa_profile目录创建符号链接
                    createSymlink(outDefaultSrcSaProfileSubsystemComponentPath, path.join(srcDirPath, 'sa_profile', 'inputs', targetDirName));
                    
                    vscode.window.showInformationMessage(`[${parentFolderName}]: Compile Success`);
                    return true;
                } catch (error) {
                    componentCompileLogger.errorWithStack(`Error creating subdirectories or symlinks: ${error instanceof Error ? error.message : 'Unknown error'}`);
                    vscode.window.showErrorMessage(`[${parentFolderName}]: Compile success, but create symlinks failed, please check the log for more information`);
                    return false;
                }
                
            } else {
                componentCompileLogger.error(`[${parentFolderName}]: Compile failed, ${result.message}`);
                vscode.window.showErrorMessage(`[${parentFolderName}]: Compile failed, please check the log for more information`);
                return false;
            }
        
        } catch (error) {
            componentCompileLogger.errorWithStack(`Error compiling: ${error instanceof Error ? error.message : 'Unknown error'}`);
            vscode.window.showErrorMessage(`[${parentFolderName}]: Compile failed, please check the log for more information`);
            return false;
        } finally {
            isCompiling = false;
        }
    });
}

function createSymlink(sourcePath: string, targetPath: string) {
    // 复制文件而不是创建符号链接
    if (fs.existsSync(sourcePath)) {
        // 确保目标路径的父目录存在
        createDirectory(path.dirname(targetPath));
        // 如果目标已存在，先删除
        if (fs.existsSync(targetPath)) {
            if (fs.lstatSync(targetPath).isDirectory()) {
                fs.rmSync(targetPath, { recursive: true, force: true });
            } else {
                fs.unlinkSync(targetPath);
            }
        }
        
        // 复制目录
        copyDirectory(sourcePath, targetPath);
        componentCompileLogger.info(`Copied directory from ${sourcePath} to ${targetPath}`);
    } else {
        componentCompileLogger.info(`Source directory not found: ${sourcePath}`);
    }
}

/**
 * 递归复制目录及其内容
 * 
 * @param source 源目录路径
 * @param target 目标目录路径
 */
function copyDirectory(source: string, target: string) {
    // 创建目标目录
    fs.mkdirSync(target, { recursive: true });
    
    // 读取源目录中的所有文件和子目录
    const items = fs.readdirSync(source);
    
    for (const item of items) {
        const sourcePath = path.join(source, item);
        const targetPath = path.join(target, item);
        
        // 检查是文件还是目录
        const stat = fs.statSync(sourcePath);
        
        if (stat.isDirectory()) {
            // 递归复制子目录
            copyDirectory(sourcePath, targetPath);
        } else {
            // 复制文件
            fs.copyFileSync(sourcePath, targetPath);
        }
    }
}

/**
 * 创建目录
 * 
 * @param buildDirPath 构建目录路径
 * @returns 输出目录路径
 */
function createDirectory(dirPath: string): string {
    if (!fs.existsSync(dirPath)) {
        fs.mkdirSync(dirPath, { recursive: true });
        componentCompileLogger.info(`Created directory at: ${dirPath}`);
    }
    return dirPath;
}

/**
 * 创建或清空build目录
 * 
 * @param dirPath 目录路径
 * @returns 构建目录路径
 */
function createBuildDirectory(dirPath: string): string {
    // 创建或清空build目录
    const buildDirPath = path.join(dirPath, 'build');
    
    // 检查build目录是否存在
    if (fs.existsSync(buildDirPath)) {
        // 如果存在，清空目录内容
        fs.readdirSync(buildDirPath).forEach((file: string) => {
            const curPath = path.join(buildDirPath, file);
            if (fs.lstatSync(curPath).isDirectory()) {
                // 递归删除子目录
                fs.rmSync(curPath, { recursive: true, force: true });
            } else {
                // 删除文件
                fs.unlinkSync(curPath);
            }
        });
        componentCompileLogger.info(`Cleaned existing build directory at: ${buildDirPath}`);
    } else {
        // 如果不存在，创建目录
        fs.mkdirSync(buildDirPath);
        componentCompileLogger.info(`Created build directory at: ${buildDirPath}`);
    }
    return buildDirPath;
}






/**
 * 读取bundle.json文件，并返回componentPath，如果读取失败，则返回null
 * 
 * @param dirPath 目录路径
 * @returns 组件路径，如果读取失败，则返回null
 */
function readBundleJson(dirPath: string): string | null {
    // 读取bundle.json文件
    try {
        const bundleJsonPath = path.join(dirPath, 'bundle.json');
        if (fs.existsSync(bundleJsonPath)) {
            const bundleContent = fs.readFileSync(bundleJsonPath, 'utf8');
            const bundleJson = JSON.parse(bundleContent);
            
            // 提取subsystem和name
            if (bundleJson.component && bundleJson.component.subsystem && bundleJson.component.name) {
                const subsystem = bundleJson.component.subsystem;
                const name = bundleJson.component.name;
                const componentPath = `${subsystem}/${name}`;
                
                componentCompileLogger.info(`Component path from bundle.json: ${componentPath}`);
                return componentPath;
            }
        }
        return null;
    } catch (error) {
        componentCompileLogger.errorWithStack(`Error reading bundle.json: ${error instanceof Error ? error.message : 'Unknown error'}`);
        return null;
    }
}

/**
 * 执行命令并记录日志
 * 
 * @param state 输入状态
 * @param dirPath 目录路径
 * @param parentFolderName 父文件夹名称
 * @param repoRootPath 仓库根目录
 * @param token 取消令牌
 * @returns 执行结果 
 */
async function executeAndLogCommand(state: State, dirPath: string, parentFolderName: string, repoRootPath: string, token?: vscode.CancellationToken): Promise<{ status: string; message: string }> {
    componentCompileLogger.info(`Begin to compile, dirPath is ${dirPath}, parentFolderName is ${parentFolderName}`);
    
    let commandStr = `hb build ${parentFolderName}`;
    if (state.compileSourceCode) {
        commandStr += ' -i';
    }
    if (state.compileTestCode) {
        commandStr += ' -t';
    }

    // 获取命令执行的工作目录（文件所在目录）
    const workingDir = repoRootPath;
    
    // 将命令字符串分解为命令和参数
    const parts = commandStr.split(' ');
    const command = parts[0];
    const args = parts.slice(1);
    
    const process = spawn(command, args, { 
        cwd: workingDir 
    });

    compileLogger.show();
    // 实时处理标准输出
    process.stdout.on('data', (data) => {
        const output = data.toString().trim();
        if (output) {
            output.split('\n').forEach((line: string) => {
                if (line.trim()) {
                    compileLogger.append(`  ${line}`);
                }
            });
        }
    });
    
    // 实时处理标准错误
    process.stderr.on('data', (data) => {
        const output = data.toString().trim();
        if (output) {
            output.split('\n').forEach((line: string) => {
                if (line.trim()) {
                    compileLogger.append(`  ${line}`);
                }
            });
        }
    });
    
    return new Promise<{ status: string; message: string }>((resolve, reject) => {
        // 保存对取消状态的引用
        let isCancelled = false;
        
        if (token) {
            token.onCancellationRequested(() => {
                isCancelled = true;
                process.kill();
                reject({ status: 'failed', message: 'Compile cancelled by user' });
            });
        }
        
        process.on('close', (code) => {
            // 如果已经因为取消而解析了，就不要重复解析
            if (isCancelled) {
                return;
            }
            
            if (code === 0) {
                resolve({ status: 'success', message: 'Compile success' });
            } else {
                const errorMsg = `命令 ${commandStr} 执行失败，退出码: ${code}`;
                reject({ status: 'failed', message: errorMsg });
            }
        });
        
        process.on('error', (err) => {
            // 如果已经因为取消而解析了，就不要重复解析
            if (isCancelled) {
                return;
            }
            
            reject({ status: 'failed', message: `command execution error: ${err.message}` });
        });
    }).then((result: { status: string; message: string }) => {
        return result;
    }).catch((error: { status: string; message: string }) => {
        return error;
    });
}

interface State {
    title: string;
    step: number;
    totalSteps: number;
    compileSourceCode: boolean;
    compileTestCode: boolean;
    buildFormFactor: "RK3568",
}

async function collectInputs(title: string) {
    const state = {} as Partial<State>;
    const compileTypeItems: QuickPickItem[] = ['源码', '测试用例'].map(label => ({ label }));
    await MultiStepInput.run(input => pickCompileType(compileTypeItems, title, input, state));
    return state as State;
}

async function pickCompileType(
    items: QuickPickItem[],
    title: string, input: MultiStepInput, state: Partial<State>) {
    const pick = await input.showQuickPick(true, {
        title,
        step: 1,
        totalSteps: 2,
        placeholder: 'Pick a compile type',
        items: items,
        activeItem: undefined,
        buttons: [],
        shouldResume: () => Promise.resolve(false)
    });

    const selectedItems = pick.map(item => item.label);
    if (selectedItems.length === 0) {
        componentCompileLogger.errorWithStack('No compile type selected');
        state.compileSourceCode = undefined;
        state.compileTestCode = undefined;
    } else {
        // Update state based on selections
        state.compileSourceCode = selectedItems.includes('源码');
        state.compileTestCode = selectedItems.includes('测试用例');
    }

    // Move to the next step if needed
    const factorItems: QuickPickItem[] = ['RK3568'].map(label => ({ label }));
    return (input: MultiStepInput) => pickBuildFormFactor( factorItems, 'Pick a build form factor', input, state);
}


async function pickBuildFormFactor(
    items: QuickPickItem[],
    title: string, input: MultiStepInput, state: Partial<State>) {

    // 仅展示
    await input.showQuickPick(false, {
        title,
        step: 2,
        totalSteps: 2,
        placeholder: 'Pick a build form factor',
        items: items,
        activeItem: undefined,
        buttons: [],
        shouldResume: () => Promise.resolve(false)
    });
}


type InputStep = (input: MultiStepInput) => Thenable<InputStep | void>;

class InputFlowAction {
	static back = new InputFlowAction();
	static cancel = new InputFlowAction();
	static resume = new InputFlowAction();
}

interface QuickPickParameters<T extends QuickPickItem> {
	title: string;
	step: number;
	totalSteps: number;
	items: T[];
	activeItem?: T;
	ignoreFocusOut?: boolean;
	placeholder: string;
	buttons?: QuickInputButton[];
	shouldResume: () => Thenable<boolean>;
}

class MultiStepInput {

	static async run(start: InputStep) {
		const input = new MultiStepInput();
		return input.stepThrough(start);
	}

	private current?: QuickInput;
	private steps: InputStep[] = [];

	private async stepThrough(start: InputStep) {
		let step: InputStep | void = start;
		while (step) {
			this.steps.push(step);
			// dispose是会有延迟的
			if (this.current) {
				this.current.enabled = false;
				this.current.busy = true;
			}
			try {
				step = await step(this);
			} catch (err) {
				if (err === InputFlowAction.back) {
					this.steps.pop();
					step = this.steps.pop();
				} else if (err === InputFlowAction.resume) {
					step = this.steps.pop();
				} else if (err === InputFlowAction.cancel) {
					step = undefined;
				} else {
					throw err;
				}
			}
		}
		if (this.current) {
			this.current.dispose();
		}
	}

	async showQuickPick<T extends QuickPickItem, P extends QuickPickParameters<T>>(canSelectMany: boolean, {title, step, totalSteps, items, activeItem, ignoreFocusOut, placeholder, buttons, shouldResume }: P) {
		const disposables: Disposable[] = [];
		try {
			return await new Promise<T[] | (P extends { buttons: (infer I)[] } ? I : never)>((resolve, reject) => {
				const input = window.createQuickPick<T>();
				input.canSelectMany = canSelectMany;
				input.title = title;
				input.step = step;
				input.totalSteps = totalSteps;
				input.ignoreFocusOut = ignoreFocusOut ?? false;
				input.placeholder = placeholder;
				input.items = items;
			
				if (activeItem) {
					input.activeItems = [activeItem];
				}
				input.buttons = [
					...(this.steps.length > 1 ? [QuickInputButtons.Back] : []),
					...(buttons || [])
				];

				disposables.push(
					input.onDidChangeSelection(items => {
						if (items.length > 0) {
							resolve(Array.from(items));
						} else {
							// 如果没有选择项，更新 placeholder 提示用户
							window.showInformationMessage('Please select at least one item');
						}
					})
				);

				if (this.current) {
					this.current.dispose();
				}
				this.current = input;
				this.current.show();
			});
		} finally {
			disposables.forEach(d => d.dispose());
		}
	}
}